https://www.ncdc.noaa.gov/isd

Development_Edition_Do_Not_Use_Until_Released

RNOAA R test for the ISD/DS3505 Datasets

Pardon the typos; they are legion.

To begin with we will use the “rnoaa” library.

It is also handy to have the “lubridate” library to manage the time coordinate data.

ISD Parsing is sone with the “isdparser”

You will need to install all three

I also included the “openair” library for wind roses

Something to be aware of before we start.

These are an merger of several report message types, METARs (Hourlies), SPECIs (special reports that supplement the METARs), and 3-hrly but more comprehensive SYNOPs. Most are derived for use at airports and thus have more than one cloud field (low, middle and high). The data archiving is designed to house any of the messages in one single record.

For some fields like temperature, pressure, humidity and wind speed, this isn’t too much of a problem. But for other fields like cloud and precip and significant weather that will vary with the type of report message.

library("lubridate")
library("isdparser")
library("rnoaa")
library("openair")
library("ncdf4")

To target a given station location you will need two catalog codes

The USAF Code and the WBAN code.

If you didn’t work in the old Asheville Federal Building in the 80s before they moved and don’t know what file cabinet the station history info is kept, there is hope.

If you have the latitude and longitude and radius in KM you can pull those fields from using the isd_station_search function.

stations_near_targ = isd_stations_search(lat    =   44,  # degrees_north
                                         lon    = -103,  # degrees_east
                                         radius =  100)  # km
file_title_string = "KRAP"
name_of_station   = "Rapid City Regional Airport"
print(stations_near_targ)

So for Rapid City Regional Airport (KRAP) which probably has the best reporting fidelity since it’s a First Order Station for NOAA, and for your period, you will want to use the following stationID pair.

I am not sure as to the quality or reliability of Elsworth (KRCA), Custer (KCUT) or Spearfish (KSPF) since they are not affiliated with an NWS office.

target_usaf = 726620
target_wban =  24090
target_year =   2010
station_name_label = paste(name_of_station, 
                           target_year)
output_file_name = paste(file_title_string,
                         target_year,
                         ".csv",
                         sep="")

To extract the data (and this can take a few minutes since you are digging on the NOAA servers…)

use the isd command following this example. The original data is kept in a compressed “tarball” with one tarball per year.

This also has the option for multiprocessing but you probably don’t need it.

targ_data = isd(usaf = target_usaf,  # your usaf number
                wban = target_wban,  # your wban number
                year = target_year,  # your year
                progress=TRUE)       # shows prograss as you go
found in cache
print(targ_data)

You will want to fix the date. In the ISD format it is split between calendar day (UTC time always) and clock hour (UTC time always)

This can be done with the lubridate function ymd_hm()

targ_data$date_time = ymd_hm(sprintf("%s %s",
                                      as.character(targ_data$date),
                                      targ_data$time))
targ_data$date = targ_data$date_time 

The data is parsed as strings. So you’ll have to use the as.numeric() a lot Missing fields are typically ID’ed as 9999 in the original data

targ_data$temperature[targ_data$temperature == "+9999"]                   = NA 
targ_data$temperature_dewpoint[targ_data$temperature_dewpoint == "+9999"] = NA
targ_data$air_pressure[targ_data$air_pressure == "99999"]                 = NA
targ_data$wind_speed[targ_data$wind_speed == "9999"]                     = NA
targ_data$wind_direction[targ_data$wind_direction == "999"]             = NA

Some of these fields, but not all, are managed with the isd_transform() function with respect to scaling

temp/dew point (deg C) converted from 10ths of degree mean sea level pressure (hPa) converted from 10ths of hPa wind speed (m s-1) converted from 10ths of m s-1 wind direction (compass decimal degrees)

Precip is also processed but this data set is an merger of both hourly, 3-hrly, 6-hrly, 12-hrly, 24-hrly data depending on the report message that’s being archived. T

precip_workspace_time_interval = as.numeric(targ_data$AA1_period_quantity_hrs)
precip_workspace_depth         = as.numeric(targ_data$AA1_depth)
precip_workspace_depth[precip_workspace_depth == 9999]                   = NA 
precip_workspace_depth_01hrly =  precip_workspace_depth
precip_workspace_depth_03hrly =  precip_workspace_depth
precip_workspace_depth_06hrly =  precip_workspace_depth
precip_workspace_depth_12hrly =  precip_workspace_depth
precip_workspace_depth_24hrly =  precip_workspace_depth
precip_workspace_depth_01hrly[precip_workspace_time_interval != 01]  =  NA
precip_workspace_depth_03hrly[precip_workspace_time_interval != 03]  =  NA
precip_workspace_depth_06hrly[precip_workspace_time_interval != 06]  =  NA
precip_workspace_depth_12hrly[precip_workspace_time_interval != 12]  =  NA
precip_workspace_depth_24hrly[precip_workspace_time_interval != 24]  =  NA
targ_data$precip_01hr = precip_workspace_depth_01hrly
targ_data$precip_03hr = precip_workspace_depth_03hrly
targ_data$precip_06hr = precip_workspace_depth_06hrly
targ_data$precip_12hr = precip_workspace_depth_12hrly
targ_data$precip_24hr = precip_workspace_depth_24hrly
targ_data = isd_transform(targ_data)
unknown timezone '%Y%m%d'
# patch the wind direction so it's 0 degrees when the wind speed is missing
targ_data$wind_direction[targ_data$wind_speed == 0]             = 0

Cloud Cover is held in several positions in the dataset. Once again this is because the data is designed for use for aerodrome use.

I am setting it up to give you the “GF1 group” which is the total cloud cover for all flight levels. These fields are often archived in “octaves” or eights and I’ll be converting them to fractions for you. IN cases of “obscured sky” caused by fog or smoke, I will label it 100% for total obscured sky or 50% covered for partial obscured skies.
print

targ_data$GF1_total_cloud_cover_fraction = as.numeric(targ_data$GF1_coverage)
# 00: None, SKC or CLR
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==00] = 0.00
# 01: One okta - 1/10 or less but not zero
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==01] = 1.0 / 8.0
# 02: Two oktas - 2/10 ‑ 3/10, or FEW
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==02] = 2.0 / 8.0
# 03: Three oktas - 4/10
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==03] = 3.0 / 8.0
# 04: Four oktas - 5/10, or SCT
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==04] = 4.0 / 8.0
# 05: Five oktas - 6/10
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==05] = 5.0 / 8.0
# 06: Six oktas - 7/10 ‑ 8/10
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==06] = 6.0 / 8.0
# 07: Seven oktas - 9/10 or more but not 10/10, or BKN
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==07] = 7.0 / 8.0
# 08: Eight oktas - 10/10, or OVC
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==08] = 8.0 / 8.0
# 09: Sky obscured, or cloud amount cannot be estimated
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==09] = 8.0 / 8.0
# 10: Partial obscuration
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==10] = 4.0 / 8.0
# 11: Thin scattered
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==11] = 2.0 / 8.0
# 12: Scattered
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==12] = 4.0 / 8.0
# 13: Dark scattered
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==13] = 5.0 / 8.0
# 14: Thin broken
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==14] = 6.0 / 8.0
# 15: Broken
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==15] = 7.0 / 8.0
# 16: Dark broken
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==16] = 8.0 / 8.0
# 17: Thin overcast
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==17] = 4.0 / 8.0
# 18: Overcast
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==18] = 8.0 / 8.0
# 19: Dark overcast
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==19] = 8.0 / 8.0
# 99: Missing
targ_data$GF1_total_cloud_cover_fraction[targ_data$GF1_total_cloud_cover_fraction==99] = NA

And to plot everything out…

Temperature

plot(x       = targ_data$date_time,
     y       = targ_data$temperature, 
     type    = "l", 
     col     = "red", 
     lwd     = 1.5, 
     cex.lab = 1.25,
     xlab    = "Date", 
     ylab    = "Temperature (deg C)",
     main    = station_name_label)

Dew Point

plot(x       = targ_data$date_time,
     y       = targ_data$temperature_dewpoint, 
     type    = "l", 
     col     = "darkgreen", 
     lwd     = 1.5, 
     cex.lab = 1.25,
     xlab    = "Date", 
     ylab    = "Dew Point (deg C)",
     main    = station_name_label)

Cloud Fraction

plot(x       = targ_data$date_time,
     y       = targ_data$GF1_total_cloud_cover_fraction, 
     type    = "l", 
     col     = "grey", 
     lwd     = 1.5, 
     cex.lab = 1.25,
     xlab    = "Date", 
     ylab    = "Cloud Cover (fraction)",
     main    = station_name_label)

Pressure

plot(x       = targ_data$date_time,
     y       = targ_data$air_pressure, 
     type    = "l", 
     col     = "blue", 
     lwd     = 1.5, 
     cex.lab = 1.25,
     xlab    = "Date", 
     ylab    = "MSL Pressure (mb)",
     main    = station_name_label)

Wind Speed

plot(x       = targ_data$date_time,
     y       = targ_data$wind_speed, 
     type    = "l", 
     col     = "darkblue", 
     lwd     = 1.5, 
     cex.lab = 1.25,
     xlab    = "Date", 
     ylab    = "Wind Speed (m s-1)",
     main    = station_name_label)

Wind Rose

windrose_frame = data.frame(date      = targ_data$date_time,
                            ws        = targ_data$wind_speed,
                            wd        = targ_data$wind_direction,
                            longitude = targ_data$longitude,
                            latitude  = targ_data$latitude)
windRose(mydata     = windrose_frame, 
         ws         = "ws", 
         wd         = "wd",
         type       = "year",
         hemisphere = "northern",
         main       = station_name_label)

windRose(mydata     = windrose_frame, 
         ws         = "ws", 
         wd         = "wd",
         type       = "season",
         hemisphere = "northern",
         main       = station_name_label)

Finally let’s put everything into a single time frame since we have observations at several times. We can interpolate the data to the nearest hour.

We do this by first creating a regularly spaced time vector

start_date = as.POSIXct(paste(target_year,
                              "-01-01 00:00:00 UTC",
                              sep=""),
                        tz = "UTC")
end_date   = as.POSIXct(paste(target_year,
                              "-12-31 00:00:00 UTC",
                              sep=""),
                        tz = "UTC")
hour_time = seq.POSIXt(from = start_date,
                        to   = end_date,
                        by   = "1 hour",
                        tz   = "UTC")

We then interpolate between the various fields and frame them…

targ_time_series                  = data.frame(date = hour_time)
targ_time_series$temperature_degC = approx(x      = targ_data$date_time,
                                           y      = targ_data$temperature,
                                           method = "linear",
                                           xout   = hour_time)$y
                              
targ_time_series$dewpoint_degC    = approx(x      = targ_data$date_time,
                                           y      = targ_data$temperature_dewpoint,
                                           method = "linear",
                                           xout   = hour_time)$y 
                              
targ_time_series$cloud_fraction   = approx(x      = targ_data$date_time,
                                           y      = targ_data$GF1_total_cloud_cover_fraction,
                                           method = "linear",
                                           xout   = hour_time)$y 
                              
targ_time_series$press_msl_hPa    = approx(x      = targ_data$date_time,
                                           y      = targ_data$air_pressure,
                                           method = "linear",
                                           xout   = hour_time)$y
                              
targ_time_series$wind_spd_ms      = approx(x      = targ_data$date_time,
                                           y      = targ_data$wind_speed,
                                           method = "linear",
                                           xout   = hour_time)$y
                              
targ_time_series$wind_dir_degrees  = approx(x     = targ_data$date_time,
                                           y      = targ_data$wind_direction,
                                           method = "linear",
                                           xout   = hour_time)$y
targ_time_series$ISD_precip_01hr   = approx(x      = targ_data$date_time,
                                            y      = targ_data$precip_01hr,
                                            method = "constant",
                                            xout   = hour_time)$y
targ_time_series$ISD_precip_03hr   = approx(x      = targ_data$date_time,
                                            y      = targ_data$precip_03hr,
                                            method = "constant",
                                            xout   = hour_time)$y
targ_time_series$ISD_precip_06hr   = approx(x      = targ_data$date_time,
                                            y      = targ_data$precip_06hr,
                                            method = "constant",
                                            xout   = hour_time)$y
#targ_time_series$ISD_precip_12hr   = approx(x      = targ_data$date_time,
#                                            y      = targ_data$precip_12hr,
#                                            method = "constant",
#                                            xout   = hour_time)$y
targ_time_series$ISD_precip_24hr   = approx(x      = targ_data$date_time,
                                            y      = targ_data$precip_24hr,
                                            method = "constant",
                                            xout   = hour_time)$y
print(targ_time_series)

And send it to an ASCII file

output_file_name = paste(file_title_string,
                         "_HOURLY_",
                         target_year,
                         ".csv",
                         sep="")
write.table(x    = targ_time_series, 
            file = output_file_name, 
            sep  =", ",
            row.names = FALSE)

Now let’s make a “raw” datafile for all observations regardless what part of the hour they are taken.

targ_time_series_raw                      = data.frame(date = targ_data$date_time)
targ_time_series_raw$temperature_dewpoint = targ_data$temperature_dewpoint
targ_time_series_raw$cloud_cover_fraction = targ_data$GF1_total_cloud_cover_fraction
targ_time_series_raw$air_pressure         = targ_data$air_pressure
targ_time_series_raw$wind_speed       = targ_data$wind_speed
targ_time_series_raw$wind_dir_degrees = targ_data$wind_direction
targ_time_series_raw$ISD_precip_01hr = targ_data$precip_01hr
targ_time_series_raw$ISD_precip_03hr = targ_data$precip_03hr
targ_time_series_raw$ISD_precip_06hr = targ_data$precip_06hr
targ_time_series_raw$ISD_precip_12hr = targ_data$precip_12hr
targ_time_series_raw$ISD_precip_24hr = targ_data$precip_24hr
  
output_file_name = paste(file_title_string,
                         "_RAW_",
                         target_year,
                         ".csv",
                         sep="")
write.table(x    = targ_time_series_raw, 
            file = output_file_name, 
            sep  =", ",
            row.names = FALSE)

This section prepeares the output for a formal NetCDF file.

This section is in preparation.

LS0tCnRpdGxlOiAicm5vYWEgIE5vdGVib29rIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpodHRwczovL3d3dy5uY2RjLm5vYWEuZ292L2lzZAoKX19EZXZlbG9wbWVudF9FZGl0aW9uX0RvX05vdF9Vc2VfVW50aWxfUmVsZWFzZWRfXwoKUk5PQUEgUiB0ZXN0IGZvciB0aGUgSVNEL0RTMzUwNSBEYXRhc2V0cwoKUGFyZG9uIHRoZSB0eXBvczsgdGhleSBhcmUgbGVnaW9uLgoKVG8gYmVnaW4gd2l0aCB3ZSB3aWxsIHVzZSB0aGUgInJub2FhIiBsaWJyYXJ5LgoKSXQgaXMgYWxzbyBoYW5keSB0byBoYXZlIHRoZSAibHVicmlkYXRlIiBsaWJyYXJ5IHRvIG1hbmFnZSB0aGUgdGltZSBjb29yZGluYXRlIGRhdGEuICAKCklTRCBQYXJzaW5nIGlzIHNvbmUgd2l0aCB0aGUgImlzZHBhcnNlciIKCllvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCBhbGwgdGhyZWUKCkkgYWxzbyBpbmNsdWRlZCAgdGhlICJvcGVuYWlyIiBsaWJyYXJ5IGZvciB3aW5kIHJvc2VzCgpTb21ldGhpbmcgdG8gYmUgYXdhcmUgb2YgYmVmb3JlIHdlIHN0YXJ0LiAgCgpUaGVzZSBhcmUgYW4gbWVyZ2VyIG9mIHNldmVyYWwgcmVwb3J0IG1lc3NhZ2UgdHlwZXMsIE1FVEFScyAoSG91cmxpZXMpLCBTUEVDSXMgKHNwZWNpYWwgcmVwb3J0cyB0aGF0IHN1cHBsZW1lbnQgdGhlIE1FVEFScyksIGFuZCAzLWhybHkgYnV0IG1vcmUgY29tcHJlaGVuc2l2ZSBTWU5PUHMuICBNb3N0IGFyZSBkZXJpdmVkIGZvciB1c2UgYXQgYWlycG9ydHMgYW5kIHRodXMgaGF2ZSBtb3JlIHRoYW4gb25lIGNsb3VkIGZpZWxkIChsb3csIG1pZGRsZSBhbmQgaGlnaCkuIFRoZSBkYXRhIGFyY2hpdmluZyBpcyBkZXNpZ25lZCB0byBob3VzZSBhbnkgb2YgdGhlIG1lc3NhZ2VzIGluIG9uZSBzaW5nbGUgcmVjb3JkLgoKRm9yIHNvbWUgZmllbGRzIGxpa2UgdGVtcGVyYXR1cmUsIHByZXNzdXJlLCBodW1pZGl0eSBhbmQgd2luZCBzcGVlZCwgdGhpcyBpc24ndCB0b28gbXVjaCBvZiBhIHByb2JsZW0uICBCdXQgZm9yIG90aGVyIGZpZWxkcyBsaWtlIGNsb3VkIGFuZCBwcmVjaXAgYW5kIHNpZ25pZmljYW50IHdlYXRoZXIgdGhhdCB3aWxsIHZhcnkgd2l0aCB0aGUgdHlwZSBvZiByZXBvcnQgbWVzc2FnZS4KCmBgYHtyfQpsaWJyYXJ5KCJsdWJyaWRhdGUiKQpsaWJyYXJ5KCJpc2RwYXJzZXIiKQpsaWJyYXJ5KCJybm9hYSIpCmxpYnJhcnkoIm9wZW5haXIiKQpsaWJyYXJ5KCJuY2RmNCIpCmBgYAoKVG8gdGFyZ2V0IGEgZ2l2ZW4gc3RhdGlvbiBsb2NhdGlvbiB5b3Ugd2lsbCBuZWVkIHR3byBjYXRhbG9nIGNvZGVzCgpUaGUgVVNBRiBDb2RlIGFuZCB0aGUgV0JBTiBjb2RlLgoKSWYgeW91IGRpZG4ndCB3b3JrIGluIHRoZSBvbGQgQXNoZXZpbGxlIEZlZGVyYWwgQnVpbGRpbmcgaW4gdGhlIDgwcyBiZWZvcmUgdGhleSBtb3ZlZCBhbmQgZG9uJ3Qga25vdyB3aGF0IGZpbGUgY2FiaW5ldCB0aGUgc3RhdGlvbiBoaXN0b3J5IGluZm8gaXMga2VwdCwgdGhlcmUgaXMgaG9wZS4KCklmIHlvdSBoYXZlIHRoZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGFuZCByYWRpdXMgaW4gS00geW91IGNhbiBwdWxsIHRob3NlIGZpZWxkcyBmcm9tICB1c2luZyB0aGUgaXNkX3N0YXRpb25fc2VhcmNoIGZ1bmN0aW9uLgoKYGBge3J9CnN0YXRpb25zX25lYXJfdGFyZyA9IGlzZF9zdGF0aW9uc19zZWFyY2gobGF0ICAgID0gICA0NCwgICMgZGVncmVlc19ub3J0aAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvbiAgICA9IC0xMDMsICAjIGRlZ3JlZXNfZWFzdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9ICAxMDApICAjIGttCgpmaWxlX3RpdGxlX3N0cmluZyA9ICJLUkFQIgpuYW1lX29mX3N0YXRpb24gICA9ICJSYXBpZCBDaXR5IFJlZ2lvbmFsIEFpcnBvcnQiCgpwcmludChzdGF0aW9uc19uZWFyX3RhcmcpCmBgYAoKU28gZm9yIFJhcGlkIENpdHkgUmVnaW9uYWwgQWlycG9ydCAoS1JBUCkgd2hpY2ggcHJvYmFibHkgaGFzIHRoZSBiZXN0IHJlcG9ydGluZyBmaWRlbGl0eSBzaW5jZSBpdCdzIGEgRmlyc3QgT3JkZXIgU3RhdGlvbiBmb3IgTk9BQSwgYW5kIGZvciB5b3VyIHBlcmlvZCwgeW91IHdpbGwgd2FudCB0byB1c2UgdGhlIGZvbGxvd2luZyBzdGF0aW9uSUQgcGFpci4KCkkgYW0gbm90IHN1cmUgYXMgdG8gdGhlIHF1YWxpdHkgb3IgcmVsaWFiaWxpdHkgb2YgRWxzd29ydGggKEtSQ0EpLCBDdXN0ZXIgKEtDVVQpIG9yIFNwZWFyZmlzaCAoS1NQRikgc2luY2UgdGhleSBhcmUgbm90IGFmZmlsaWF0ZWQgd2l0aCBhbiBOV1Mgb2ZmaWNlLgoKYGBge3J9CnRhcmdldF91c2FmID0gNzI2NjIwCnRhcmdldF93YmFuID0gIDI0MDkwCnRhcmdldF95ZWFyID0gICAyMDEwCgpzdGF0aW9uX25hbWVfbGFiZWwgPSBwYXN0ZShuYW1lX29mX3N0YXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfeWVhcikKCm91dHB1dF9maWxlX25hbWUgPSBwYXN0ZShmaWxlX3RpdGxlX3N0cmluZywKICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldF95ZWFyLAogICAgICAgICAgICAgICAgICAgICAgICAgIi5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKQoKYGBgCgpUbyBleHRyYWN0IHRoZSBkYXRhIChhbmQgdGhpcyBjYW4gdGFrZSBhIGZldyBtaW51dGVzIHNpbmNlIHlvdSBhcmUgZGlnZ2luZyBvbiB0aGUgTk9BQSBzZXJ2ZXJzLi4uKSAKCnVzZSB0aGUgaXNkIGNvbW1hbmQgZm9sbG93aW5nIHRoaXMgZXhhbXBsZS4gIFRoZSBvcmlnaW5hbCBkYXRhIGlzIGtlcHQgaW4gYSBjb21wcmVzc2VkICJ0YXJiYWxsIiB3aXRoIG9uZSB0YXJiYWxsIHBlciB5ZWFyLgoKVGhpcyBhbHNvIGhhcyB0aGUgb3B0aW9uIGZvciBtdWx0aXByb2Nlc3NpbmcgYnV0IHlvdSBwcm9iYWJseSBkb24ndCBuZWVkIGl0LgoKYGBge3J9CnRhcmdfZGF0YSA9IGlzZCh1c2FmID0gdGFyZ2V0X3VzYWYsICAjIHlvdXIgdXNhZiBudW1iZXIKICAgICAgICAgICAgICAgIHdiYW4gPSB0YXJnZXRfd2JhbiwgICMgeW91ciB3YmFuIG51bWJlcgogICAgICAgICAgICAgICAgeWVhciA9IHRhcmdldF95ZWFyLCAgIyB5b3VyIHllYXIKICAgICAgICAgICAgICAgIHByb2dyZXNzPVRSVUUpICAgICAgICMgc2hvd3MgcHJvZ3Jhc3MgYXMgeW91IGdvCgpwcmludCh0YXJnX2RhdGEpCgpgYGAKCgoKWW91IHdpbGwgd2FudCB0byBmaXggdGhlIGRhdGUuICBJbiB0aGUgSVNEIGZvcm1hdCBpdCBpcyBzcGxpdCBiZXR3ZWVuIGNhbGVuZGFyIGRheSAoVVRDIHRpbWUgYWx3YXlzKSBhbmQgY2xvY2sgaG91ciAoVVRDIHRpbWUgYWx3YXlzKQoKVGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBsdWJyaWRhdGUgZnVuY3Rpb24geW1kX2htKCkKCmBgYHtyfQp0YXJnX2RhdGEkZGF0ZV90aW1lID0geW1kX2htKHNwcmludGYoIiVzICVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIodGFyZ19kYXRhJGRhdGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdfZGF0YSR0aW1lKSkKCnRhcmdfZGF0YSRkYXRlID0gdGFyZ19kYXRhJGRhdGVfdGltZSAKCmBgYAoKClRoZSBkYXRhIGlzIHBhcnNlZCBhcyBzdHJpbmdzLiAgU28geW91J2xsIGhhdmUgdG8gdXNlIHRoZSBhcy5udW1lcmljKCkgYSBsb3QKTWlzc2luZyBmaWVsZHMgYXJlIHR5cGljYWxseSBJRCdlZCBhcyA5OTk5IGluIHRoZSBvcmlnaW5hbCBkYXRhCgpgYGB7cn0KCgp0YXJnX2RhdGEkdGVtcGVyYXR1cmVbdGFyZ19kYXRhJHRlbXBlcmF0dXJlID09ICIrOTk5OSJdICAgICAgICAgICAgICAgICAgID0gTkEgCgp0YXJnX2RhdGEkdGVtcGVyYXR1cmVfZGV3cG9pbnRbdGFyZ19kYXRhJHRlbXBlcmF0dXJlX2Rld3BvaW50ID09ICIrOTk5OSJdID0gTkEKCnRhcmdfZGF0YSRhaXJfcHJlc3N1cmVbdGFyZ19kYXRhJGFpcl9wcmVzc3VyZSA9PSAiOTk5OTkiXSAgICAgICAgICAgICAgICAgPSBOQQoKdGFyZ19kYXRhJHdpbmRfc3BlZWRbdGFyZ19kYXRhJHdpbmRfc3BlZWQgPT0gIjk5OTkiXSAgICAgICAgICAgICAgICAgICAgID0gTkEKCnRhcmdfZGF0YSR3aW5kX2RpcmVjdGlvblt0YXJnX2RhdGEkd2luZF9kaXJlY3Rpb24gPT0gIjk5OSJdICAgICAgICAgICAgID0gTkEKCgoKCmBgYAoKU29tZSBvZiB0aGVzZSBmaWVsZHMsIGJ1dCBub3QgYWxsLCBhcmUgbWFuYWdlZCB3aXRoIHRoZSBpc2RfdHJhbnNmb3JtKCkgZnVuY3Rpb24gd2l0aCByZXNwZWN0IHRvIHNjYWxpbmcKCnRlbXAvZGV3IHBvaW50IChkZWcgQykgY29udmVydGVkIGZyb20gMTB0aHMgb2YgZGVncmVlCm1lYW4gc2VhIGxldmVsIHByZXNzdXJlIChoUGEpIGNvbnZlcnRlZCBmcm9tIDEwdGhzIG9mIGhQYQp3aW5kIHNwZWVkICAgICAobSBzLTEpIGNvbnZlcnRlZCBmcm9tIDEwdGhzIG9mIG0gcy0xCndpbmQgZGlyZWN0aW9uIChjb21wYXNzIGRlY2ltYWwgZGVncmVlcykgCgpQcmVjaXAgaXMgYWxzbyBwcm9jZXNzZWQgYnV0IHRoaXMgZGF0YSBzZXQgaXMgYW4gbWVyZ2VyIG9mIGJvdGggaG91cmx5LCAzLWhybHksIDYtaHJseSwgMTItaHJseSwgMjQtaHJseSBkYXRhIGRlcGVuZGluZyBvbiB0aGUgcmVwb3J0IG1lc3NhZ2UgdGhhdCdzIGJlaW5nIGFyY2hpdmVkLiAgVAoKYGBge3J9CgpwcmVjaXBfd29ya3NwYWNlX3RpbWVfaW50ZXJ2YWwgPSBhcy5udW1lcmljKHRhcmdfZGF0YSRBQTFfcGVyaW9kX3F1YW50aXR5X2hycykKCgpwcmVjaXBfd29ya3NwYWNlX2RlcHRoICAgICAgICAgPSBhcy5udW1lcmljKHRhcmdfZGF0YSRBQTFfZGVwdGgpCnByZWNpcF93b3Jrc3BhY2VfZGVwdGhbcHJlY2lwX3dvcmtzcGFjZV9kZXB0aCA9PSA5OTk5XSAgICAgICAgICAgICAgICAgICA9IE5BIAoKcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8wMWhybHkgPSAgcHJlY2lwX3dvcmtzcGFjZV9kZXB0aApwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzAzaHJseSA9ICBwcmVjaXBfd29ya3NwYWNlX2RlcHRoCnByZWNpcF93b3Jrc3BhY2VfZGVwdGhfMDZocmx5ID0gIHByZWNpcF93b3Jrc3BhY2VfZGVwdGgKcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8xMmhybHkgPSAgcHJlY2lwX3dvcmtzcGFjZV9kZXB0aApwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzI0aHJseSA9ICBwcmVjaXBfd29ya3NwYWNlX2RlcHRoCgpwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzAxaHJseVtwcmVjaXBfd29ya3NwYWNlX3RpbWVfaW50ZXJ2YWwgIT0gMDFdICA9ICBOQQpwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzAzaHJseVtwcmVjaXBfd29ya3NwYWNlX3RpbWVfaW50ZXJ2YWwgIT0gMDNdICA9ICBOQQpwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzA2aHJseVtwcmVjaXBfd29ya3NwYWNlX3RpbWVfaW50ZXJ2YWwgIT0gMDZdICA9ICBOQQpwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzEyaHJseVtwcmVjaXBfd29ya3NwYWNlX3RpbWVfaW50ZXJ2YWwgIT0gMTJdICA9ICBOQQpwcmVjaXBfd29ya3NwYWNlX2RlcHRoXzI0aHJseVtwcmVjaXBfd29ya3NwYWNlX3RpbWVfaW50ZXJ2YWwgIT0gMjRdICA9ICBOQQoKdGFyZ19kYXRhJHByZWNpcF8wMWhyID0gcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8wMWhybHkKdGFyZ19kYXRhJHByZWNpcF8wM2hyID0gcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8wM2hybHkKdGFyZ19kYXRhJHByZWNpcF8wNmhyID0gcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8wNmhybHkKdGFyZ19kYXRhJHByZWNpcF8xMmhyID0gcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8xMmhybHkKdGFyZ19kYXRhJHByZWNpcF8yNGhyID0gcHJlY2lwX3dvcmtzcGFjZV9kZXB0aF8yNGhybHkKCmBgYAoKCgoKCmBgYHtyfQoKdGFyZ19kYXRhID0gaXNkX3RyYW5zZm9ybSh0YXJnX2RhdGEpCgojIHBhdGNoIHRoZSB3aW5kIGRpcmVjdGlvbiBzbyBpdCdzIDAgZGVncmVlcyB3aGVuIHRoZSB3aW5kIHNwZWVkIGlzIG1pc3NpbmcKCnRhcmdfZGF0YSR3aW5kX2RpcmVjdGlvblt0YXJnX2RhdGEkd2luZF9zcGVlZCA9PSAwXSAgICAgICAgICAgICA9IDAKCgpgYGAKCgoKQ2xvdWQgQ292ZXIgaXMgaGVsZCBpbiBzZXZlcmFsIHBvc2l0aW9ucyBpbiB0aGUgZGF0YXNldC4gIE9uY2UgYWdhaW4gdGhpcyBpcyBiZWNhdXNlIHRoZSBkYXRhIGlzIGRlc2lnbmVkIGZvciB1c2UgZm9yIGFlcm9kcm9tZSB1c2UuICAKCkkgYW0gc2V0dGluZyBpdCB1cCB0byBnaXZlIHlvdSB0aGUgIkdGMSBncm91cCIgd2hpY2ggaXMgdGhlIHRvdGFsIGNsb3VkIGNvdmVyIGZvciBhbGwgZmxpZ2h0IGxldmVscy4gIFRoZXNlIGZpZWxkcyBhcmUgb2Z0ZW4gYXJjaGl2ZWQgaW4gIm9jdGF2ZXMiIG9yIGVpZ2h0cyBhbmQgSSdsbCBiZSBjb252ZXJ0aW5nIHRoZW0gdG8gZnJhY3Rpb25zIGZvciB5b3UuICBJTiBjYXNlcyBvZiAib2JzY3VyZWQgc2t5IiBjYXVzZWQgYnkgZm9nIG9yIHNtb2tlLCBJIHdpbGwgbGFiZWwgaXQgMTAwJSBmb3IgdG90YWwgb2JzY3VyZWQgc2t5IG9yIDUwJSBjb3ZlcmVkIGZvciBwYXJ0aWFsIG9ic2N1cmVkIHNraWVzLiAgICAKcHJpbnQKCmBgYHtyfQoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbiA9IGFzLm51bWVyaWModGFyZ19kYXRhJEdGMV9jb3ZlcmFnZSkKCiMgMDA6IE5vbmUsIFNLQyBvciBDTFIKCnRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb25bdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbj09MDBdID0gMC4wMAoKIyAwMTogT25lIG9rdGEgLSAxLzEwIG9yIGxlc3MgYnV0IG5vdCB6ZXJvCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTAxXSA9IDEuMCAvIDguMAoKIyAwMjogVHdvIG9rdGFzIC0gMi8xMCDigJEgMy8xMCwgb3IgRkVXCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTAyXSA9IDIuMCAvIDguMAoKIyAwMzogVGhyZWUgb2t0YXMgLSA0LzEwCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTAzXSA9IDMuMCAvIDguMAoKIyAwNDogRm91ciBva3RhcyAtIDUvMTAsIG9yIFNDVAoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvblt0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uPT0wNF0gPSA0LjAgLyA4LjAKCiMgMDU6IEZpdmUgb2t0YXMgLSA2LzEwCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTA1XSA9IDUuMCAvIDguMAoKIyAwNjogU2l4IG9rdGFzIC0gNy8xMCDigJEgOC8xMAoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvblt0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uPT0wNl0gPSA2LjAgLyA4LjAKCiMgMDc6IFNldmVuIG9rdGFzIC0gOS8xMCBvciBtb3JlIGJ1dCBub3QgMTAvMTAsIG9yIEJLTgoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvblt0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uPT0wN10gPSA3LjAgLyA4LjAKCiMgMDg6IEVpZ2h0IG9rdGFzIC0gMTAvMTAsIG9yIE9WQwoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvblt0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uPT0wOF0gPSA4LjAgLyA4LjAKCiMgMDk6IFNreSBvYnNjdXJlZCwgb3IgY2xvdWQgYW1vdW50IGNhbm5vdCBiZSBlc3RpbWF0ZWQKCnRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb25bdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbj09MDldID0gOC4wIC8gOC4wCgojIDEwOiBQYXJ0aWFsIG9ic2N1cmF0aW9uCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTEwXSA9IDQuMCAvIDguMAoKIyAxMTogVGhpbiBzY2F0dGVyZWQKCnRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb25bdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbj09MTFdID0gMi4wIC8gOC4wCgojIDEyOiBTY2F0dGVyZWQKCnRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb25bdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbj09MTJdID0gNC4wIC8gOC4wCgojIDEzOiBEYXJrIHNjYXR0ZXJlZAoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvblt0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uPT0xM10gPSA1LjAgLyA4LjAKCiMgMTQ6IFRoaW4gYnJva2VuCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTE0XSA9IDYuMCAvIDguMAoKIyAxNTogQnJva2VuCgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTE1XSA9IDcuMCAvIDguMAoKIyAxNjogRGFyayBicm9rZW4KCnRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb25bdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbj09MTZdID0gOC4wIC8gOC4wCgojIDE3OiBUaGluIG92ZXJjYXN0Cgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTE3XSA9IDQuMCAvIDguMAoKIyAxODogT3ZlcmNhc3QKCnRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb25bdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbj09MThdID0gOC4wIC8gOC4wCgojIDE5OiBEYXJrIG92ZXJjYXN0Cgp0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uW3RhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb249PTE5XSA9IDguMCAvIDguMAoKIyA5OTogTWlzc2luZwoKdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvblt0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uPT05OV0gPSBOQQoKCmBgYAoKCkFuZCB0byBwbG90IGV2ZXJ5dGhpbmcgb3V0Li4uCgpUZW1wZXJhdHVyZQoKYGBge3J9CgpwbG90KHggICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgIHkgICAgICAgPSB0YXJnX2RhdGEkdGVtcGVyYXR1cmUsIAogICAgIHR5cGUgICAgPSAibCIsIAogICAgIGNvbCAgICAgPSAicmVkIiwgCiAgICAgbHdkICAgICA9IDEuNSwgCiAgICAgY2V4LmxhYiA9IDEuMjUsCiAgICAgeGxhYiAgICA9ICJEYXRlIiwgCiAgICAgeWxhYiAgICA9ICJUZW1wZXJhdHVyZSAoZGVnIEMpIiwKICAgICBtYWluICAgID0gc3RhdGlvbl9uYW1lX2xhYmVsKQoKYGBgCgoKRGV3IFBvaW50CgpgYGB7cn0KCgpwbG90KHggICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgIHkgICAgICAgPSB0YXJnX2RhdGEkdGVtcGVyYXR1cmVfZGV3cG9pbnQsIAogICAgIHR5cGUgICAgPSAibCIsIAogICAgIGNvbCAgICAgPSAiZGFya2dyZWVuIiwgCiAgICAgbHdkICAgICA9IDEuNSwgCiAgICAgY2V4LmxhYiA9IDEuMjUsCiAgICAgeGxhYiAgICA9ICJEYXRlIiwgCiAgICAgeWxhYiAgICA9ICJEZXcgUG9pbnQgKGRlZyBDKSIsCiAgICAgbWFpbiAgICA9IHN0YXRpb25fbmFtZV9sYWJlbCkKCmBgYAoKQ2xvdWQgRnJhY3Rpb24gCmBgYHtyfQoKCnBsb3QoeCAgICAgICA9IHRhcmdfZGF0YSRkYXRlX3RpbWUsCiAgICAgeSAgICAgICA9IHRhcmdfZGF0YSRHRjFfdG90YWxfY2xvdWRfY292ZXJfZnJhY3Rpb24sIAogICAgIHR5cGUgICAgPSAibCIsIAogICAgIGNvbCAgICAgPSAiZ3JleSIsIAogICAgIGx3ZCAgICAgPSAxLjUsIAogICAgIGNleC5sYWIgPSAxLjI1LAogICAgIHhsYWIgICAgPSAiRGF0ZSIsIAogICAgIHlsYWIgICAgPSAiQ2xvdWQgQ292ZXIgKGZyYWN0aW9uKSIsCiAgICAgbWFpbiAgICA9IHN0YXRpb25fbmFtZV9sYWJlbCkKCmBgYAoKCgoKUHJlc3N1cmUgCgpgYGB7cn0KCgpwbG90KHggICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgIHkgICAgICAgPSB0YXJnX2RhdGEkYWlyX3ByZXNzdXJlLCAKICAgICB0eXBlICAgID0gImwiLCAKICAgICBjb2wgICAgID0gImJsdWUiLCAKICAgICBsd2QgICAgID0gMS41LCAKICAgICBjZXgubGFiID0gMS4yNSwKICAgICB4bGFiICAgID0gIkRhdGUiLCAKICAgICB5bGFiICAgID0gIk1TTCBQcmVzc3VyZSAobWIpIiwKICAgICBtYWluICAgID0gc3RhdGlvbl9uYW1lX2xhYmVsKQoKYGBgCgoKCldpbmQgU3BlZWQgCgpgYGB7cn0KCgpwbG90KHggICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgIHkgICAgICAgPSB0YXJnX2RhdGEkd2luZF9zcGVlZCwgCiAgICAgdHlwZSAgICA9ICJsIiwgCiAgICAgY29sICAgICA9ICJkYXJrYmx1ZSIsIAogICAgIGx3ZCAgICAgPSAxLjUsIAogICAgIGNleC5sYWIgPSAxLjI1LAogICAgIHhsYWIgICAgPSAiRGF0ZSIsIAogICAgIHlsYWIgICAgPSAiV2luZCBTcGVlZCAobSBzLTEpIiwKICAgICBtYWluICAgID0gc3RhdGlvbl9uYW1lX2xhYmVsKQoKYGBgCgoKV2luZCBSb3NlIAoKYGBge3J9Cgp3aW5kcm9zZV9mcmFtZSA9IGRhdGEuZnJhbWUoZGF0ZSAgICAgID0gdGFyZ19kYXRhJGRhdGVfdGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdzICAgICAgICA9IHRhcmdfZGF0YSR3aW5kX3NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2QgICAgICAgID0gdGFyZ19kYXRhJHdpbmRfZGlyZWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9uZ2l0dWRlID0gdGFyZ19kYXRhJGxvbmdpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGl0dWRlICA9IHRhcmdfZGF0YSRsYXRpdHVkZSkKCgoKd2luZFJvc2UobXlkYXRhICAgICA9IHdpbmRyb3NlX2ZyYW1lLCAKICAgICAgICAgd3MgICAgICAgICA9ICJ3cyIsIAogICAgICAgICB3ZCAgICAgICAgID0gIndkIiwKICAgICAgICAgdHlwZSAgICAgICA9ICJ5ZWFyIiwKICAgICAgICAgaGVtaXNwaGVyZSA9ICJub3J0aGVybiIsCiAgICAgICAgIG1haW4gICAgICAgPSBzdGF0aW9uX25hbWVfbGFiZWwpCgoKd2luZFJvc2UobXlkYXRhICAgICA9IHdpbmRyb3NlX2ZyYW1lLCAKICAgICAgICAgd3MgICAgICAgICA9ICJ3cyIsIAogICAgICAgICB3ZCAgICAgICAgID0gIndkIiwKICAgICAgICAgdHlwZSAgICAgICA9ICJzZWFzb24iLAogICAgICAgICBoZW1pc3BoZXJlID0gIm5vcnRoZXJuIiwKICAgICAgICAgbWFpbiAgICAgICA9IHN0YXRpb25fbmFtZV9sYWJlbCkKCgpgYGAKCkZpbmFsbHkgbGV0J3MgcHV0IGV2ZXJ5dGhpbmcgaW50byBhIHNpbmdsZSB0aW1lIGZyYW1lIHNpbmNlIHdlIGhhdmUgb2JzZXJ2YXRpb25zIGF0IHNldmVyYWwgdGltZXMuICBXZSBjYW4gaW50ZXJwb2xhdGUgdGhlIGRhdGEgdG8gdGhlIG5lYXJlc3QgaG91ci4KCldlIGRvIHRoaXMgYnkgZmlyc3QgY3JlYXRpbmcgYSByZWd1bGFybHkgc3BhY2VkIHRpbWUgdmVjdG9yCgoKYGBge3J9CgpzdGFydF9kYXRlID0gYXMuUE9TSVhjdChwYXN0ZSh0YXJnZXRfeWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wMS0wMSAwMDowMDowMCBVVEMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IiIpLAogICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJVVEMiKQoKZW5kX2RhdGUgICA9IGFzLlBPU0lYY3QocGFzdGUodGFyZ2V0X3llYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMTItMzEgMDA6MDA6MDAgVVRDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgdHogPSAiVVRDIikKCmhvdXJfdGltZSA9IHNlcS5QT1NJWHQoZnJvbSA9IHN0YXJ0X2RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgIHRvICAgPSBlbmRfZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgYnkgICA9ICIxIGhvdXIiLAogICAgICAgICAgICAgICAgICAgICAgICB0eiAgID0gIlVUQyIpCgoKCmBgYAoKV2UgdGhlbiBpbnRlcnBvbGF0ZSBiZXR3ZWVuIHRoZSB2YXJpb3VzIGZpZWxkcyBhbmQgZnJhbWUgdGhlbS4uLiAKCmBgYHtyfQoKdGFyZ190aW1lX3NlcmllcyAgICAgICAgICAgICAgICAgID0gZGF0YS5mcmFtZShkYXRlID0gaG91cl90aW1lKQoKdGFyZ190aW1lX3NlcmllcyR0ZW1wZXJhdHVyZV9kZWdDID0gYXBwcm94KHggICAgICA9IHRhcmdfZGF0YSRkYXRlX3RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ICAgICAgPSB0YXJnX2RhdGEkdGVtcGVyYXR1cmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAibGluZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKdGFyZ190aW1lX3NlcmllcyRkZXdwb2ludF9kZWdDICAgID0gYXBwcm94KHggICAgICA9IHRhcmdfZGF0YSRkYXRlX3RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ICAgICAgPSB0YXJnX2RhdGEkdGVtcGVyYXR1cmVfZGV3cG9pbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAibGluZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCnRhcmdfdGltZV9zZXJpZXMkY2xvdWRfZnJhY3Rpb24gICA9IGFwcHJveCh4ICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSAgICAgID0gdGFyZ19kYXRhJEdGMV90b3RhbF9jbG91ZF9jb3Zlcl9mcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJsaW5lYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeG91dCAgID0gaG91cl90aW1lKSR5IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKdGFyZ190aW1lX3NlcmllcyRwcmVzc19tc2xfaFBhICAgID0gYXBwcm94KHggICAgICA9IHRhcmdfZGF0YSRkYXRlX3RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ICAgICAgPSB0YXJnX2RhdGEkYWlyX3ByZXNzdXJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImxpbmVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4b3V0ICAgPSBob3VyX3RpbWUpJHkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCnRhcmdfdGltZV9zZXJpZXMkd2luZF9zcGRfbXMgICAgICA9IGFwcHJveCh4ICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSAgICAgID0gdGFyZ19kYXRhJHdpbmRfc3BlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAibGluZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKdGFyZ190aW1lX3NlcmllcyR3aW5kX2Rpcl9kZWdyZWVzICA9IGFwcHJveCh4ICAgICA9IHRhcmdfZGF0YSRkYXRlX3RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ICAgICAgPSB0YXJnX2RhdGEkd2luZF9kaXJlY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAibGluZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeQoKdGFyZ190aW1lX3NlcmllcyRJU0RfcHJlY2lwXzAxaHIgICA9IGFwcHJveCh4ICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgICAgICA9IHRhcmdfZGF0YSRwcmVjaXBfMDFociwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY29uc3RhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeQoKdGFyZ190aW1lX3NlcmllcyRJU0RfcHJlY2lwXzAzaHIgICA9IGFwcHJveCh4ICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgICAgICA9IHRhcmdfZGF0YSRwcmVjaXBfMDNociwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY29uc3RhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeQoKdGFyZ190aW1lX3NlcmllcyRJU0RfcHJlY2lwXzA2aHIgICA9IGFwcHJveCh4ICAgICAgPSB0YXJnX2RhdGEkZGF0ZV90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgICAgICA9IHRhcmdfZGF0YSRwcmVjaXBfMDZociwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY29uc3RhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhvdXQgICA9IGhvdXJfdGltZSkkeQoKI3RhcmdfdGltZV9zZXJpZXMkSVNEX3ByZWNpcF8xMmhyICAgPSBhcHByb3goeCAgICAgID0gdGFyZ19kYXRhJGRhdGVfdGltZSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSAgICAgID0gdGFyZ19kYXRhJHByZWNpcF8xMmhyLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY29uc3RhbnQiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4b3V0ICAgPSBob3VyX3RpbWUpJHkKCnRhcmdfdGltZV9zZXJpZXMkSVNEX3ByZWNpcF8yNGhyICAgPSBhcHByb3goeCAgICAgID0gdGFyZ19kYXRhJGRhdGVfdGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ICAgICAgPSB0YXJnX2RhdGEkcHJlY2lwXzI0aHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImNvbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4b3V0ICAgPSBob3VyX3RpbWUpJHkKcHJpbnQodGFyZ190aW1lX3NlcmllcykKYGBgCgpBbmQgc2VuZCBpdCB0byBhbiBBU0NJSSBmaWxlCgpgYGB7cn0KCm91dHB1dF9maWxlX25hbWUgPSBwYXN0ZShmaWxlX3RpdGxlX3N0cmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICJfSE9VUkxZXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfeWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICIuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikKCndyaXRlLnRhYmxlKHggICAgPSB0YXJnX3RpbWVfc2VyaWVzLCAKICAgICAgICAgICAgZmlsZSA9IG91dHB1dF9maWxlX25hbWUsIAogICAgICAgICAgICBzZXAgID0iLCAiLAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCk5vdyBsZXQncyBtYWtlIGEgInJhdyIgZGF0YWZpbGUgZm9yIGFsbCBvYnNlcnZhdGlvbnMgcmVnYXJkbGVzcyB3aGF0IHBhcnQgb2YgdGhlIGhvdXIgdGhleSBhcmUgdGFrZW4uCgpgYGB7cn0KCnRhcmdfdGltZV9zZXJpZXNfcmF3ICAgICAgICAgICAgICAgICAgICAgID0gZGF0YS5mcmFtZShkYXRlID0gdGFyZ19kYXRhJGRhdGVfdGltZSkKdGFyZ190aW1lX3Nlcmllc19yYXckdGVtcGVyYXR1cmVfZGV3cG9pbnQgPSB0YXJnX2RhdGEkdGVtcGVyYXR1cmVfZGV3cG9pbnQKdGFyZ190aW1lX3Nlcmllc19yYXckY2xvdWRfY292ZXJfZnJhY3Rpb24gPSB0YXJnX2RhdGEkR0YxX3RvdGFsX2Nsb3VkX2NvdmVyX2ZyYWN0aW9uCnRhcmdfdGltZV9zZXJpZXNfcmF3JGFpcl9wcmVzc3VyZSAgICAgICAgID0gdGFyZ19kYXRhJGFpcl9wcmVzc3VyZQoKdGFyZ190aW1lX3Nlcmllc19yYXckd2luZF9zcGVlZCAgICAgICA9IHRhcmdfZGF0YSR3aW5kX3NwZWVkCnRhcmdfdGltZV9zZXJpZXNfcmF3JHdpbmRfZGlyX2RlZ3JlZXMgPSB0YXJnX2RhdGEkd2luZF9kaXJlY3Rpb24KCnRhcmdfdGltZV9zZXJpZXNfcmF3JElTRF9wcmVjaXBfMDFociA9IHRhcmdfZGF0YSRwcmVjaXBfMDFocgp0YXJnX3RpbWVfc2VyaWVzX3JhdyRJU0RfcHJlY2lwXzAzaHIgPSB0YXJnX2RhdGEkcHJlY2lwXzAzaHIKdGFyZ190aW1lX3Nlcmllc19yYXckSVNEX3ByZWNpcF8wNmhyID0gdGFyZ19kYXRhJHByZWNpcF8wNmhyCnRhcmdfdGltZV9zZXJpZXNfcmF3JElTRF9wcmVjaXBfMTJociA9IHRhcmdfZGF0YSRwcmVjaXBfMTJocgp0YXJnX3RpbWVfc2VyaWVzX3JhdyRJU0RfcHJlY2lwXzI0aHIgPSB0YXJnX2RhdGEkcHJlY2lwXzI0aHIKCiAgCm91dHB1dF9maWxlX25hbWUgPSBwYXN0ZShmaWxlX3RpdGxlX3N0cmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICJfUkFXXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfeWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICIuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikKCndyaXRlLnRhYmxlKHggICAgPSB0YXJnX3RpbWVfc2VyaWVzX3JhdywgCiAgICAgICAgICAgIGZpbGUgPSBvdXRwdXRfZmlsZV9uYW1lLCAKICAgICAgICAgICAgc2VwICA9IiwgIiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCgpgYGAKCgpUaGlzIHNlY3Rpb24gcHJlcGVhcmVzIHRoZSBvdXRwdXQgZm9yIGEgZm9ybWFsIE5ldENERiBmaWxlLgoKVGhpcyBzZWN0aW9uIGlzIGluIHByZXBhcmF0aW9uLiAKCgpgYGB7cn0KCmBgYAoK